# include "AudioEncoder.h"

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include "libavutil/time.h"
#include "libswresample/swresample.h"
}

# include "EncoderFilter.h"

AudioEncoder::AudioEncoder(AVSampleFormat _input_format, int _sample_rate, int _bit_rate)
{
	avdevice_register_all();

	this->m_audio_encode_codec = const_cast<AVCodec*>(avcodec_find_encoder(AV_CODEC_ID_AAC));
	if (this->m_audio_encode_codec == nullptr) {
		// TODO log

		return;
	}
	this->m_audio_encode_codec_ctx = avcodec_alloc_context3(this->m_audio_encode_codec);
	this->m_audio_encode_codec_ctx->sample_rate = _sample_rate;
	this->m_audio_encode_codec_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
	this->m_audio_encode_codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
	this->m_audio_encode_codec_ctx->bit_rate = _bit_rate;
	this->m_audio_encode_codec_ctx->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
	if (avcodec_open2(this->m_audio_encode_codec_ctx, this->m_audio_encode_codec, NULL) != 0) {
		// TODO log

		return;
	}
	this->m_audio_encode_frame = av_frame_alloc();
	this->m_audio_encode_frame->pts = 0;
	this->m_audio_encode_frame->nb_samples = this->m_audio_encode_codec_ctx->frame_size;
	this->m_audio_encode_frame->format = this->m_audio_encode_codec_ctx->sample_fmt;
	this->m_audio_encode_frame->ch_layout = this->m_audio_encode_codec_ctx->ch_layout;
	this->m_audio_encode_frame->sample_rate = this->m_audio_encode_codec_ctx->sample_rate;
	auto _buff_size = av_samples_get_buffer_size(NULL, this->m_audio_encode_frame->ch_layout.nb_channels, this->m_audio_encode_codec_ctx->frame_size, this->m_audio_encode_codec_ctx->sample_fmt, 1);
	this->m_audio_frame_buf = (uint8_t*)av_malloc(_buff_size);
	int r = avcodec_fill_audio_frame(this->m_audio_encode_frame, this->m_audio_encode_frame->ch_layout.nb_channels, this->m_audio_encode_codec_ctx->sample_fmt, this->m_audio_frame_buf, _buff_size, 1);

	this->m_audio_encode_packet = av_packet_alloc();
	this->m_audio_encode_buffer = new uint8_t[10240];

	if (swr_alloc_set_opts2(&this->m_audio_swr_ctx, &this->m_audio_encode_codec_ctx->ch_layout, this->m_audio_encode_codec_ctx->sample_fmt, this->m_audio_encode_codec_ctx->sample_rate, &this->m_audio_encode_codec_ctx->ch_layout, _input_format, this->m_audio_encode_codec_ctx->sample_rate, 0, nullptr) != 0) {
		// TODO log

		return;
	}
	swr_init(this->m_audio_swr_ctx);
	this->m_audio_swr_buffer = new uint8_t[10240];

	this->m_ready_encode = true;
	// TODO log

	return;
}

uint8_t* AudioEncoder::getEncodeCodecCtx(size_t* _size) const
{
	if (_size != nullptr) {
		*_size = sizeof(AVCodecContext);
	}
	return  reinterpret_cast<uint8_t*>(this->m_audio_encode_codec_ctx);
}

void AudioEncoder::addFilter(EncoderFilter* _filter)
{
	if (this->m_filter == nullptr) {
		this->m_filter = _filter;
	}
	else {
		this->m_filter->addFilter(_filter);
	}
}

void AudioEncoder::onPcmData(uint8_t* _data, size_t _sample_count, size_t _sample_size)
{
	if (!this->m_ready_encode) {
		return;
	}
	// 转换
	size_t size = _sample_count * _sample_size;
	uint8_t* in[2] = { _data, nullptr };
	uint8_t* out[2] = { this->m_audio_swr_buffer, this->m_audio_swr_buffer + size };
	int count = swr_convert(this->m_audio_swr_ctx, out, _sample_count, const_cast<const uint8_t**>(in), _sample_count);
	if (this->m_filter != nullptr) {
		this->m_filter->afterAudioSwr(out[0], _sample_count, this);
	}
	// 缓存保证传入编码器数据为1024 * 4  aac规定
	memcpy_s(this->m_audio_encode_buffer + this->m_audio_encode_buffer_size, size, out[0], size);
	this->m_audio_encode_buffer_size += size;
	while (this->m_audio_encode_buffer_size >= SENDFRAMESIZE) {
		// this->m_audio_encode_frame->pts += 1024;
		memcpy_s(m_audio_frame_buf, SENDFRAMESIZE, this->m_audio_encode_buffer, SENDFRAMESIZE);
		memcpy_s(m_audio_frame_buf + SENDFRAMESIZE, SENDFRAMESIZE, this->m_audio_encode_buffer, SENDFRAMESIZE);
		avcodec_send_frame(this->m_audio_encode_codec_ctx, this->m_audio_encode_frame);
		memcpy_s(this->m_audio_encode_buffer, SENDFRAMESIZE, this->m_audio_encode_buffer + SENDFRAMESIZE, SENDFRAMESIZE);
		this->m_audio_encode_buffer_size -= SENDFRAMESIZE;
	}
	while (avcodec_receive_packet(this->m_audio_encode_codec_ctx, this->m_audio_encode_packet) >= 0) {
		if (this->m_filter != nullptr) {
			this->m_filter->afterAudioEncode(this->m_audio_encode_packet);
		}
		av_packet_unref(this->m_audio_encode_packet);
	}
}

AudioEncoder::~AudioEncoder()
{
	this->m_ready_encode = false;

	// delete this->m_filter;

	if (this->m_audio_swr_buffer != nullptr) {
		delete[] this->m_audio_swr_buffer;
		this->m_audio_swr_buffer = nullptr;
	}

	if (this->m_audio_encode_buffer != nullptr) {
		delete[] this->m_audio_encode_buffer;
		this->m_audio_encode_buffer = nullptr;
	}

	if (this->m_audio_encode_codec_ctx != nullptr) {
		avcodec_send_frame(this->m_audio_encode_codec_ctx, nullptr);
		while (avcodec_receive_packet(this->m_audio_encode_codec_ctx, this->m_audio_encode_packet) >= 0) {

		}
		av_packet_unref(this->m_audio_encode_packet);
	}

	if (this->m_audio_encode_packet != nullptr) {
		av_packet_free(&this->m_audio_encode_packet);
	}

	if (this->m_audio_encode_frame != nullptr) {
		av_frame_free(&this->m_audio_encode_frame);
	}

	if (this->m_audio_frame_buf != nullptr) {
		av_free(this->m_audio_frame_buf);
		this->m_audio_frame_buf = nullptr;
	}

	if (this->m_audio_encode_codec_ctx != nullptr) {
		avcodec_free_context(&this->m_audio_encode_codec_ctx);
		this->m_audio_encode_codec_ctx = nullptr;
	}

	if (this->m_audio_swr_ctx != nullptr) {
		swr_free(&this->m_audio_swr_ctx);
		m_audio_swr_ctx = nullptr;
	}
}


